/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_random.h"

#include <stddef.h>

#include "ola_base_utils.h"
#include "ola_error.h"
#include "ola_pal_os.h"
#include "ola_pal_random.h"
#include "ola_log.h"
#include "securec.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/entropy.h"
#include "mbedtls/entropy_poll.h"

#define OLA_RANDOM_CUSTOM_STR "OLA"

static bool g_olaRandInit = false;
static mbedtls_entropy_context g_olaEntropy;
static mbedtls_ctr_drbg_context g_olaCtrDrbg;

static int OlaEntropyPoll(void *data, unsigned char *output, size_t len, size_t *outLen)
{
    unsigned long long tick;

    if (output == NULL || outLen == NULL) {
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    if (OlaGetTime(&tick) != 0) {
        OlaLogWarning("get tick error");
        return OLA_RANDOM_ERR_ENTROPY_CALLBACK;
    }

    if (memcpy_s(output, len, &tick, sizeof(tick)) != EOK) {
        OlaLogWarning("memcpy_s error");
        return OLA_SECUREC_ERR_MEMCOPY;
    }

    *outLen = sizeof(tick);
    return OLA_OK;
}

int OlaRandInit(void)
{
    int ret;

    if (g_olaRandInit) {
        return OLA_OK;
    }

    /* 初始化熵和种子结构体 */
    mbedtls_entropy_init(&g_olaEntropy);
    mbedtls_ctr_drbg_init(&g_olaCtrDrbg);

    /* 添加熵源接口，设置熵源属性 */
    ret = mbedtls_entropy_add_source(&g_olaEntropy, OlaEntropyPoll, NULL,
        MBEDTLS_ENTROPY_MIN_HARDCLOCK, MBEDTLS_ENTROPY_SOURCE_STRONG);
    if (ret != 0) {
        OlaLogWarning("add source error");
        return OLA_RANDOM_ERR_ADD_SOURCE;
    }

    /* 设置种子信息 */
    ret = mbedtls_ctr_drbg_seed(&g_olaCtrDrbg, mbedtls_entropy_func, &g_olaEntropy,
        (const unsigned char *)OLA_RANDOM_CUSTOM_STR, strlen(OLA_RANDOM_CUSTOM_STR));
    if (ret != 0) {
        OlaLogWarning("seed error");
        return OLA_RANDOM_ERR_CTR_DRBG_SEED;
    }

    /* 设置种子模式 */
    mbedtls_ctr_drbg_set_prediction_resistance(&g_olaCtrDrbg, MBEDTLS_CTR_DRBG_PR_OFF);

    g_olaRandInit = true;
    return OLA_OK;
}

int OlaRand(unsigned char *rand, unsigned int size)
{
    if ((rand == NULL) || (size == 0)) {
        OlaLogError("invalid outBuf or len, len[%u]", size);
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    /* 优选硬件随机数，次选软件随机数 */
    if (OlaTrng(rand, size) == OLA_OK) {
        return OLA_OK;
    }

    if (!g_olaRandInit) {
        if (OlaRandInit() != OLA_OK) {
            return OLA_RANDOM_ERR_INIT;
        }
    }

    if (mbedtls_ctr_drbg_random((unsigned char *)&g_olaCtrDrbg, rand, size) != 0) {
        OlaLogError("get random error");
        return OLA_MBEDTLS_ERR_GEN_RANDOM;
    }

    return OLA_OK;
}